#include "String.h"
#include <math.h>

String::String() : m_str(0), m_count(0)
{
}

int	String::getTokenCount(const String& delim) const
{
	if( m_count == 0 )
		return 0;

	//Pointer to our delimiters
	const char* d = delim.cstr();
	// Number of delimiters
	unsigned short dCount = delim.length();

	// Initialize our token count
	unsigned short tokCount = 0;

	// For all of the charecters in the string
	bool wasDelim = true;

	for( int i=0; i<m_count; ++i )
	{
		// For each of the delimeters
		bool isDelim = false;
		for( int k=0; k<dCount; ++k )
		{
			if( m_str[i] == d[k] )
			{
				isDelim = true;
				break;
			}
		}

		// Only add a token whenever we go from a delimiter 
		//  charecter to a non-delimiter charecter
		if( wasDelim && !isDelim )
			tokCount++;

		wasDelim = isDelim;
	}

	return tokCount;
}

String String::getToken(unsigned int index, const String& delim) const
{
	//Pointer to our delimiters
	const char* d = delim.cstr();
	// Number of delimiters
	unsigned short dCount = delim.length();

	// Initialize our token count
	short tokCount = -1;

	// For all of the charecters in the string
	bool wasDelim = true;
	
	String token;
	for( int i=0; i<m_count && tokCount <= i; ++i )
	{
		// For each of the delimeters
		bool isDelim = false;
		for( int k=0; k<dCount; ++k )
		{
			if( m_str[i] == d[k] )
			{
				isDelim = true;
				break;
			}
		}
		
		// Only add a token whenever we go from a delimiter 
		//  charecter to a non-delimiter charecter
		if( wasDelim && !isDelim )
		{
			tokCount++;
		}

		// If we are currently on the correct token and it's not a delimiter char
		if( tokCount == index &&
			!isDelim )
		{
			// Add the charecter to the token
			token = token + m_str[i];
		}

		wasDelim = isDelim;
	}

	// Return the token
	return token;
}


String::String(const char* str) : m_str(0), m_count(0)
{
	// Get th size of the string
	m_count = strlen(str);
	// Allocate the memory for it
	m_str = new char[m_count + 1];
	// Copy the values
	strcpy(m_str, str);
}

// copy constructor
String::String(const String& str) : m_str(0), m_count(0)
{
	// Get the size of the string
	m_count = str.m_count;
	// Allocate the memory for it
	m_str = new char[m_count + 1];
	// Copy the values
	strcpy(m_str, str.m_str);
}

// conversion constructor
String::String(const int num) : m_str(0), m_count(0)
{
	if( num == 0 )
	{
		*this = String("0");
		return;
	}

	// Convert the integer to a string
	char* buff = new char[32];
	char* pbuff = buff;
	int value = num;

	// Convert a negative integer to positive
	bool neg = false;
	if( value < 0 )
	{
		neg = true;
		value = -value;
	}

	// Convert the int to a char buffer
	while( value > 0 )
	{
		*pbuff = '0' + (value % 10);
		value /= 10;
		++pbuff;
	}

	if( neg )
	{
		*pbuff = '-';
		++pbuff;
	}

	*pbuff = 0;

	// Copy the buffer into our new correctly sized buffer
	m_count = strlen(buff);
	allocate(m_count + 1);
	strcpy(m_str, buff);

	// Free our memory
	delete [] buff;


	// Reverse the value
	reverse();

}

String::String(const unsigned int num) : m_str(0), m_count(0)
{
	if( num == 0 )
	{
		*this = String("0");
		return;
	}

	// Convert the integer to a string
	char* buff = new char[32];
	char* pbuff = buff;
	int value = num;

	// Convert the int to a char buffer
	while( value > 0 )
	{
		*pbuff = '0' + (value % 10);
		value /= 10;
		++pbuff;
	}
	*pbuff = 0;

	// Copy the buffer into our new correctly sized buffer
	m_count = strlen(buff);
	allocate(m_count + 1);
	strcpy(m_str, buff);

	// Free our memory
	delete [] buff;

	// Reverse the value
	reverse();
}
String::String(const short num)				{ String((const int)num); }
String::String(const unsigned short num)	{ String((const unsigned int)num); }
String::String(const long num)				{ String((const int)num); }
String::String(const unsigned long num)		{ String((const unsigned int)num); }
String::String(const double num) : m_str(0), m_count(0)
{
	// Get the digits above and below the decimal point
	const float shift = 1000000000.0;
	int above = (int)num;
	int below = (int)((num - above) * shift);
	
	// Calculate the number of zeros to the left
	float changeBase	= 1.0f / log( 10.0f );							// Precalculate the change to base 10 (Decimal)
	int   aboveBase		= (int)( log( (float)above ) * changeBase );	// Calculate the length of the above part
	int   belowBase		= (int)( log( (float)below ) * changeBase );	// Calculate the length of the below part
	int   zeroCount		= (aboveBase + 9) - (belowBase + 1);			// Calculate the number of zero's
	
	// Remove any zero's to the right
	while( below && below % 10 == 0 )
		below /= 10;
	
	// Create the above portion of the number
	String flt( String(above) + "." );

	// Add the zero's into the number
	for( int i=0; i<zeroCount; ++i )
		flt += "0";
	
	// Add the below portion of the number
	flt += String(below);
	
	// Copy the string
	m_count = flt.m_count;
	allocate(m_count + 1);
	strcpy(m_str, flt.m_str);
}

String::String(const float num) : m_str(0), m_count(0)
{
	// Get the digits above and below the decimal point
	const float shift = 1000000.0;
	int above = (int)num;
	int below = (int)((num - above) * shift);
	
	// Calculate the number of zeros to the left
	float changeBase	= 1.0f / log( 10.0f );							// Precalculate the change to base 10 (Decimal)
	int   aboveBase		= (int)( log( (float)above ) * changeBase );	// Calculate the length of the above part
	int   belowBase		= (int)( log( (float)below ) * changeBase );	// Calculate the length of the below part
	int   zeroCount		= (aboveBase + 6) - (belowBase + 1);			// Calculate the number of zero's
	
	// Remove any zero's to the right
	while( below && below % 10 == 0 )
		below /= 10;
	
	// Create the above portion of the number
	String flt( String(above) + "." );

	// Add the zero's into the number
	for( int i=0; i<zeroCount; ++i )
		flt += "0";
	
	// Add the below portion of the number
	flt += String(below);
	
	// Copy the string
	m_count = flt.m_count;
	allocate(m_count + 1);
	strcpy(m_str, flt.m_str);
}

String::String(const bool val) : m_str(0), m_count(0)
{
	if( val )
	{
		allocate(5);
		m_count = 4;
		strcpy(m_str, "true");
	}
	else
	{
		allocate(6);
		m_count = 5;
		strcpy(m_str, "false");
	}
}

// destructor
String::~String()
{
	free();
}

const char* String::cstr() const
{
	return m_str;
}

bool String::operator<(const String& rhs) const
{
	const char* p_str = m_str;
	const char* p_rhs = rhs.cstr();

	// while the value of both pointers are not zero, and they are equal to one another
	while( *p_str && *p_rhs && *p_str == *p_rhs)
	{
		// Goto the next char
		++p_str;
		++p_rhs;
	}

	if( *p_str < *p_rhs )
		return true;
	
	return false;
}

bool String::operator>(const String& rhs) const
{
	const char* p_str = m_str;
	const char* p_rhs = rhs.cstr();

	// while the value of both pointers are not zero, and they are equal to one another
	while( *p_str && *p_rhs && *p_str == *p_rhs)
	{
		// Goto the next char
		++p_str;
		++p_rhs;
	}

	if( *p_str > *p_rhs )
		return true;
	
	return false;
}

// internal string length function
int	String::strlen(const char* str)
{
	if( !str )
		return 0;

	// Find the length of the string and return it
	int cnt = 0;
	for( ; *str != 0; ++str, ++cnt );
	return cnt;
}

// internal string copy function
void String::strcpy(char* dest, const char* src)
{
	if( !dest || !src )
		return;

	char* tmp = dest;
	// Copy the string 
	for( ; *src != 0; ++src, ++tmp )
		*tmp = *src;
	*tmp = 0;
}

// allocate a string to a set size
void String::allocate(int size)
{
	if( m_str == 0 )
	{
		m_str = new char[size];
	}
	else
	{
		free();
		m_str = new char[size];
	}
}

void String::free()
{
	if( m_str )
	{
		delete [] m_str;
		m_str = 0;
	}
}

String String::operator=(const String& rhs)
{
	// If the right hand side has no length
	if( rhs.length() == 0 )
	{
		free();
		m_count = 0;
		return *this;
	}
	// Otherwise if the length has changed
	if( m_count != rhs.length() )
	{
		// Make the size correct
		m_count = rhs.length();
		allocate(rhs.length() + 1);
	}
	// Copy the string
	strcpy(m_str, rhs.m_str);

	// Return a reference to ourselves
	return *this;
}

String String::operator+(const char rhs)
{
	// Create a temporary string
	String temp;

	// Allocate the correct size for the string
	temp.m_count = m_count + 1;
	temp.allocate(temp.m_count + 1);
	
	strcpy(temp.m_str, m_str);

	// Assign the last two values
	temp[temp.m_count - 1]	= rhs;
	temp[temp.m_count]		= 0;

	// Return the concatinated string
	return temp;
}

String String::operator+(const String& rhs)
{
	// Create a temporary string
	String temp;
	// Allocate the correct size for the string
	temp.m_count = m_count + rhs.m_count;
	temp.allocate(temp.m_count + 1);
	
	// Concatinate the two strings
	for( int i=0, k=0; i<temp.m_count; ++i )
	{
		if( i < m_count )
		{
			temp[i] = m_str[i];
		}
		else
		{
			temp[i] = rhs.m_str[k++];
		}
	}
	temp[temp.m_count] = 0;

	// Return the concatinated string
	return temp;
}

bool String::operator==(const String& rhs) const
{
	if( m_count != rhs.m_count )
		return false;
	
	for( int i=0; i<m_count; ++i )
		if( m_str[i] != rhs.m_str[i] )
			return false;

	return true;
}

char& String::operator[](int index)
{
	return m_str[index];
}

int String::length() const
{
	return m_count;
}

void charLower(char& c)
{
	if( c >= 'A' && c <= 'Z' )	// If the charecter is an upper case charecter
		c += ('a' - 'A');		// Convert the charecter from upper to lower case
}

void charUpper(char& c)
{
	if( c >= 'a' && c <= 'z' )	// If the charecter is an upper case charecter
		c -= ('a' - 'A');		// Convert the charecter from upper to lower case
}

void String::clear()
{
	free();
	m_count = 0;
}

const String& String::lower()
{
	for( int i=0; i<m_count; ++i )
		charLower(m_str[i]);
	return *this;
}

const String& String::upper()
{
	for( int i=0; i<m_count; ++i )
		charUpper(m_str[i]);
	return *this;
}

const String& String::propper()
{
	bool first = true;
	for( int i=0; i<m_count; ++i )
	{
		char& c = m_str[i];

		bool isChar = ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' );
		if( !isChar )
			first = true;

		if( first && isChar)
		{
			charUpper(c);
			first = false;
		}
		else
			charLower(c);
	}

	return *this;
}

const String& String::reverse()
{
	char* pBegin = m_str;
	char* pEnd = m_str + m_count - 1;
	while( pBegin < pEnd )
	{
		char temp = *pBegin;
		*pBegin = *pEnd;
		*pEnd = temp;
		++pBegin;
		--pEnd;
	}

	return *this;
}

void String::delChar(int index)
{
	if( m_count == 0 )
		return;
	else if( m_count == 1 )
	{
		clear();
		return;
	}
	if( index >= m_count )
		return;

	char* tmp = new char[m_count];

	// Copy first part
	for( int i=0; i<index; ++i )
		tmp[i] = m_str[i];

	// Copy second part minus charecter
	for( int i=index; i<m_count-1; ++i )
		tmp[i] = m_str[i+1];

	// Set null term char
	tmp[m_count] = 0;
	
	// Delete old string and swap pointers
	delete [] m_str;
	m_str = tmp;

	// Update the string length
	m_count--;
}

void String::backspace()
{
	if( m_count == 0 )
		return;
	else if( m_count == 1 )
	{
		clear();
		return;
	}

	// Create a new string of the correct size
	char* tmp = new char[m_count];
	
	// Copy all but the last charecter
	for( int i=0; i<m_count-1; ++i )
		tmp[i] = m_str[i];
	
	// Update the string length
	m_count--;

	// Set the null term char
	tmp[m_count] = 0;

	// Delete old string and swap pointers
	delete [] m_str;
	m_str = tmp;
}

String String::substr(int start, int end) const
{
	// Make sure our string indeces are valid
	if( start < 0 || start > m_count )
		return String();
	if( end < start || end > m_count )
		return String();
	
	// Create our new string
	String temp;
	temp.m_count = (end - start);
	temp.allocate(temp.m_count + 1);

	// Copy the sub string
	for( int i=start, c=0; i < end; ++i, ++c)
		temp.m_str[c] = m_str[i];

	// Set null term char
	temp.m_str[temp.m_count] = 0;

	// Return the sub string
	return temp;
}
